#include <asm/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <netinet/in.h>
 
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
 
#define MYPROTO NETLINK_ROUTE
#define MYMGRP RTMGRP_IPV4_ROUTE
 
int open_netlink()
{
    int sock = socket(AF_NETLINK,SOCK_RAW,MYPROTO);
    struct sockaddr_nl addr;

    memset((void *)&addr, 0, sizeof(addr));

    if (sock<0)
        return sock;
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR;
//    addr.nl_groups = RTMGRP_LINK;
    if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0)
        return -1;
    printf("sock: %d\n",sock);
    return sock;
}
 

int read_event(int sockint, int (*msg_handler)(struct sockaddr_nl *,
                                               struct nlmsghdr *))
{
    int status;
    int ret = 0;
    char buf[4096];
    struct iovec iov = { buf, sizeof buf };
    struct sockaddr_nl snl;
    struct msghdr msg = { (void*)&snl, sizeof snl, &iov, 1, NULL, 0, 0};
    struct nlmsghdr *h;
    printf("sockint: %d\n",sockint);
 
    status = recvmsg(sockint, &msg, 0);

    if(status < 0)
    {
        /* Socket non-blocking so bail out once we have read everything */
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return ret;

        /* Anything else is an error */
        printf("read_netlink: Error recvmsg: %d\n", status);
        perror("read_netlink: Error: ");
        return status;
    }
        
    if(status == 0)
    {
        printf("read_netlink: EOF\n");
    }

    /* We need to handle more than one message per 'recvmsg' */
    for(h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int)status); 
    h = NLMSG_NEXT (h, status))
    {
        /* Finish reading */
        if (h->nlmsg_type == NLMSG_DONE)
            return ret;

        /* Message is some kind of error */
        if (h->nlmsg_type == NLMSG_ERROR)
        {
            printf("read_netlink: Message is an error - decode TBD\n");
            return -1; // Error
        }

        /* Call message handler */
        if(msg_handler)
        {
            ret = (*msg_handler)(&snl, h);
            if(ret < 0)
            {
                printf("read_netlink: Message hander error %d\n", ret);
                return ret;
            }
        }
        else
        {
            printf("read_netlink: Error NULL message handler\n");
            return -1;
        }
    }



    return ret;
}

static int netlink_link_state(struct sockaddr_nl *nl, struct nlmsghdr *msg)
{
    int len;
    struct ifinfomsg *ifi;

    nl = nl;

    ifi = NLMSG_DATA(msg);

    printf("netlink_link_state: Link %s\n", 
           (ifi->ifi_flags & IFF_RUNNING)?"Up":"Down");
//    printf("netlink_link_state: family: %d\n", ifi->ifi_family);
//    if(ifi->ifi_family == AF_INET)
//    {
//         printf("Link index: %d Flags: (0x%x) ", 
//                ifi->ifi_index, ifi->ifi_flags);
//         if(ifi->ifi_flags & IFF_UP)
//             printf("IFF_UP ");
//         if(ifi->ifi_flags & IFF_BROADCAST)
//             printf("IFF_BROADCAST ");
//         if(ifi->ifi_flags & IFF_DEBUG)
//             printf("IFF_DEBUG ");
//         if(ifi->ifi_flags & IFF_LOOPBACK)
//             printf("IFF_LOOPBACK ");
//         if(ifi->ifi_flags & IFF_POINTOPOINT)
//             printf("IFF_POINTOPOINT ");
//         if(ifi->ifi_flags & IFF_NOTRAILERS)
//             printf("IFF_NOTRAILERS ");
//         if(ifi->ifi_flags & IFF_RUNNING)
//             printf("IFF_RUNNING ");
//         if(ifi->ifi_flags & IFF_NOARP)
//             printf("IFF_NOARP ");
//         if(ifi->ifi_flags & IFF_PROMISC)
//             printf("IFF_PROMISC ");
//         if(ifi->ifi_flags & IFF_ALLMULTI)
//             printf("IFF_ALLMULTI ");
//         if(ifi->ifi_flags & IFF_MASTER)
//             printf("IFF_MASTER ");
//         if(ifi->ifi_flags & IFF_SLAVE)
//             printf("IFF_SLAVE ");
//         if(ifi->ifi_flags & IFF_MULTICAST)
//             printf("IFF_MULTICAST ");
//         if(ifi->ifi_flags & IFF_PORTSEL)
//             printf("IFF_PORTSEL ");
//         if(ifi->ifi_flags & IFF_AUTOMEDIA)
//             printf("IFF_AUTOMEDIA ");
//         printf("\n");        
//    }
    return 0;
}

static int msg_handler(struct sockaddr_nl *nl, struct nlmsghdr *msg)
{
    switch (msg->nlmsg_type)
    {
        case RTM_NEWADDR:
            printf("msg_handler: RTM_NEWADDR\n");
            break;
        case RTM_DELADDR:
            printf("msg_handler: RTM_DELADDR\n");
            break;
        case RTM_NEWROUTE:
            printf("msg_handler: RTM_NEWROUTE\n");
            break;
        case RTM_DELROUTE:
            printf("msg_handler: RTM_DELROUTE\n");
            break;
        case RTM_NEWLINK:
            printf("msg_handler: RTM_NEWLINK\n");
            netlink_link_state(nl, msg);
            break;
        case RTM_DELLINK:
            printf("msg_handler: RTM_DELLINK\n");
            break;
        default:
            printf("msg_handler: Unknown netlink nlmsg_type %d\n",
                   msg->nlmsg_type);
            break;
    }
    return 0;
}


int main(int argc, char *argv[])
{
    int nls = open_netlink();

    printf("Started watching:\n");
    if (nls<0) {
        printf("Open Error!");
    }

    while (1)
        read_event(nls, msg_handler);
    return 0;
}
